<template>
  <div class="color-picker bg-white p-4 rounded shadow-lg">
    <div
      class="color-area relative w-64 h-48 mb-2"
      @mousedown="startColorSelection"
      @touchstart="startColorSelection"
    >
      <div
        class="absolute inset-0"
        :style="{ backgroundColor: hueColor }"
      ></div>
      <div
        class="absolute inset-0 bg-gradient-to-r from-white to-transparent"
      ></div>
      <div
        class="absolute inset-0 bg-gradient-to-t from-black to-transparent"
      ></div>
      <div
        class="absolute w-4 h-4 -mt-2 -ml-2 rounded-full border-2 border-white"
        :style="colorPickerStyle"
        ref="colorPicker"
      ></div>
    </div>
    <div
      class="color-slider relative w-64 h-4 mb-4"
      @mousedown="startHueSelection"
      @touchstart="startHueSelection"
    >
      <div
        class="w-full h-full bg-gradient-to-r from-red via-yellow via-green via-blue to-red rounded"
      ></div>
      <div
        class="absolute top-0 w-1 h-4 bg-white"
        :style="hueSliderStyle"
      ></div>
    </div>
    <div class="flex items-center mb-2">
      <div
        class="w-8 h-8 mr-2 rounded"
        :style="{ backgroundColor: displayColor }"
      ></div>
      <input
        type="text"
        v-model="hexColor"
        class="flex-grow px-2 py-1 border rounded"
        @input="updateFromHex"
      />
    </div>
    <div class="flex justify-between">
      <button
        @click="clearColor"
        class="px-4 py-1 bg-gray-200 rounded hover:bg-gray-300"
      >
        Clear
      </button>
      <button
        @click="confirmColor"
        class="px-4 py-1 bg-blue-500 text-white rounded hover:bg-blue-600"
      >
        OK
      </button>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from "vue";

const props = defineProps({
  modelValue: { type: String, default: "#40FF96" },
});

const emit = defineEmits(["update:modelValue", "confirm"]);

const hexColor = ref(props.modelValue);
const hue = ref(0);
const saturation = ref(100);
const value = ref(100);

const colorPicker = ref(null);

const hueColor = computed(() => `hsl(${hue.value}, 100%, 50%)`);
const displayColor = computed(
  () => `hsl(${hue.value}, ${saturation.value}%, ${value.value}%)`
);

const colorPickerStyle = computed(() => ({
  left: `${saturation.value}%`,
  top: `${100 - value.value}%`,
  backgroundColor: displayColor.value,
}));

const hueSliderStyle = computed(() => ({
  left: `${(hue.value / 360) * 100}%`,
}));

const startColorSelection = (event) => {
  event.preventDefault();
  updateColorFromEvent(event);
  window.addEventListener("mousemove", updateColorFromEvent);
  window.addEventListener("mouseup", stopColorSelection);
  window.addEventListener("touchmove", updateColorFromEvent);
  window.addEventListener("touchend", stopColorSelection);
};

const stopColorSelection = () => {
  window.removeEventListener("mousemove", updateColorFromEvent);
  window.removeEventListener("mouseup", stopColorSelection);
  window.removeEventListener("touchmove", updateColorFromEvent);
  window.removeEventListener("touchend", stopColorSelection);
};

const updateColorFromEvent = (event) => {
  const rect = event.target.getBoundingClientRect();
  const clientX = event.touches ? event.touches[0].clientX : event.clientX;
  const clientY = event.touches ? event.touches[0].clientY : event.clientY;

  let x = clientX - rect.left;
  let y = clientY - rect.top;

  x = Math.max(0, Math.min(x, rect.width));
  y = Math.max(0, Math.min(y, rect.height));

  saturation.value = (x / rect.width) * 100;
  value.value = 100 - (y / rect.height) * 100;
  updateHexFromHSV();
};

const startHueSelection = (event) => {
  event.preventDefault();
  updateHueFromEvent(event);
  window.addEventListener("mousemove", updateHueFromEvent);
  window.addEventListener("mouseup", stopHueSelection);
  window.addEventListener("touchmove", updateHueFromEvent);
  window.addEventListener("touchend", stopHueSelection);
};

const stopHueSelection = () => {
  window.removeEventListener("mousemove", updateHueFromEvent);
  window.removeEventListener("mouseup", stopHueSelection);
  window.removeEventListener("touchmove", updateHueFromEvent);
  window.removeEventListener("touchend", stopHueSelection);
};

const updateHueFromEvent = (event) => {
  const rect = event.target.getBoundingClientRect();
  const clientX = event.touches ? event.touches[0].clientX : event.clientX;
  let x = clientX - rect.left;
  x = Math.max(0, Math.min(x, rect.width));
  hue.value = Math.round((x / rect.width) * 360);
  updateHexFromHSV();
};

const updateHexFromHSV = () => {
  const h = hue.value / 360;
  const s = saturation.value / 100;
  const v = value.value / 100;

  let r, g, b;

  const i = Math.floor(h * 6);
  const f = h * 6 - i;
  const p = v * (1 - s);
  const q = v * (1 - f * s);
  const t = v * (1 - (1 - f) * s);

  switch (i % 6) {
    case 0:
      (r = v), (g = t), (b = p);
      break;
    case 1:
      (r = q), (g = v), (b = p);
      break;
    case 2:
      (r = p), (g = v), (b = t);
      break;
    case 3:
      (r = p), (g = q), (b = v);
      break;
    case 4:
      (r = t), (g = p), (b = v);
      break;
    case 5:
      (r = v), (g = p), (b = q);
      break;
  }

  r = Math.round(r * 255);
  g = Math.round(g * 255);
  b = Math.round(b * 255);

  hexColor.value = `#${((1 << 24) + (r << 16) + (g << 8) + b)
    .toString(16)
    .slice(1)
    .toUpperCase()}`;
  emit("update:modelValue", hexColor.value);
};

const updateFromHex = () => {
  const hex = hexColor.value.replace("#", "");
  const r = parseInt(hex.substr(0, 2), 16) / 255;
  const g = parseInt(hex.substr(2, 2), 16) / 255;
  const b = parseInt(hex.substr(4, 2), 16) / 255;

  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  const diff = max - min;

  value.value = max * 100;

  if (max === 0) {
    saturation.value = 0;
  } else {
    saturation.value = (diff / max) * 100;
  }

  if (diff === 0) {
    hue.value = 0;
  } else if (max === r) {
    hue.value = 60 * (((g - b) / diff) % 6);
  } else if (max === g) {
    hue.value = 60 * ((b - r) / diff + 2);
  } else {
    hue.value = 60 * ((r - g) / diff + 4);
  }

  if (hue.value < 0) hue.value += 360;

  emit("update:modelValue", hexColor.value);
};

const clearColor = () => {
  hexColor.value = "";
  hue.value = 0;
  saturation.value = 0;
  value.value = 100;
  emit("update:modelValue", hexColor.value);
};

const confirmColor = () => {
  emit("confirm", hexColor.value);
};

onMounted(() => {
  updateFromHex();
});

onUnmounted(() => {
  stopColorSelection();
  stopHueSelection();
});
</script>

<style scoped>
.color-picker {
  user-select: none;
}
</style>
